package com.erp.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * @Description:
 * @Author: 沈远哲
 * @Date: 2025/5/27 14:29
 */
@Component
public class RedisIdWorker {


    /*开始时间戳*/
    private static final long BEGIN_TIMESTAMP = 1735689600L;

    private final StringRedisTemplate stringRedisTemplate;

    /*序列号的位数*/
    private final int COUNT_BITS = 32;

    @Autowired
    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /*    public static void main(String[] args) {
        System.out.println(LocalDateTime.of(2025, 1, 1, 0, 0, 0)
                .toEpochSecond(ZoneOffset.UTC));
    }*/

    public String nextId(Integer companyId, String keyPrefix, String... slipsDate) {
        String date;
        if (slipsDate.length == 0) {
            // 1. 生成时间戳
            LocalDateTime now = LocalDateTime.now();
            long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
            long timeStamp = nowSecond - BEGIN_TIMESTAMP;

            // 2. 生成序列号
            /*若key值就只拼接用户所给的keyPrefix, 可以吗?*/
            // 2.1 获取当前日期, 精确到天

            date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
            // increment 能使当前key值自增1, 并将自增后的值返回, 若当前key不存在, 则新建并默认值为0, 该方法由Redis 保证原子性
            // 此处的警告是由于increment 方法原本返回的Long 型, 强制拆箱为long 可能空指针异常, 但此处我们知道是不可能的
            // 因为key不存在, 例如每天的第一个Id, Redis 会自动创建, 且默认值为0, 因此不必理会
        } else {
            date = slipsDate[0];
        }
        long increment = stringRedisTemplate.opsForValue().increment(companyId + ":" + keyPrefix + ":" + date);

        // 3. 拼接并返回
        // 由于返回值是long, 因此不能简单的像字符串一样拼接, 通过位运算实现
        //将时间戳左移序列号位数 位, 被左移的32位全部以0 填充, 再通过 | 运算, 将序列号添加进去
//        return timeStamp << COUNT_BITS | increment;
        /*或运算: 一个为真就是真; 初始时, 序列号所有位数为0 , 而序列号转化为二进制后, 每个位上可能为0 也可能为1,
        经过或运算后, 自动由低到高填充, 且不管是0还是1, 都能填充进去*/

        date = date.replace(":", "");
        return companyId + "-" + keyPrefix + "-" + date + "-" + increment;
    }
}